package net.zoneland.knowledge.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Preconditions;
import net.zoneland.knowledge.constant.ApproveConstant;
import net.zoneland.knowledge.constant.BizConstant;
import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.controller.bean.SubTaskOperationResult;
import net.zoneland.knowledge.controller.bean.WorkListQueryVo;
import net.zoneland.knowledge.controller.util.UserSelectItemUtils;
import net.zoneland.knowledge.enums.AttachTypeEnum;
import net.zoneland.knowledge.enums.WorkStatusEnum;
import net.zoneland.knowledge.exceptions.WorkApplyException;
import net.zoneland.knowledge.model.*;
import net.zoneland.knowledge.service.*;
import net.zoneland.knowledge.service.bean.ResultInfo;
import net.zoneland.knowledge.service.bean.SelectUser;
import net.zoneland.knowledge.service.bean.WorkApplyQueryVO;
import net.zoneland.knowledge.utils.FileUtils;
import net.zoneland.knowledge.utils.LoggerHelper;
import net.zoneland.knowledge.utils.LoginUtils;
import net.zoneland.knowledge.utils.WorkApplyOperationUtils;
import net.zoneland.uniflow.client.TaskObject;
import net.zoneland.uniflow.client.TaskQueryParam;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工单信息处理控制器.
 * @author zmq
 * @Date 2022/7/29 15:32
 */
@RestController
@RequestMapping("/api/work")
public class WorkApplyInfoController {
    /**
     * 日志.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WorkApplyInfoController.class);

    /**
     * 分任务id的参数名.
     */
    private static final String SUBTASK_ID_NAMES = "subTaskIds";

    /**
     * 任务id的参数名.
     */
    private static final String TASK_ID_NAME = "taskId";

    /**
     * 查看所有工单参数名（用章申请和任务分派的所有工单）.
     */
    private static final String OPEN_ALL_TASK_NAME = "openAll";
    /**
     * 工单id的参数名.
     */
    private static final String WORK_APPLY_ID_NAME = "workApplyId";

    /**
     * 是否上报全部后通知参数名.
     */
    private static final String NOTIFY_AFTER_REPORT_ALL = "notifyAfterReportAll";

    /**
     * 意见的参数名.
     */
    private static final String OPINION_NAME = "opinion";
    /**
     * 任务id不能为空提示语.
     */
    private static final String TASK_ID_BLANK_ERROR_MSG = "任务id不能为空！";
    /**
     * 工单对象json的参数名.
     */
    private static final String WORK_APPLY_INFO_JSON_NAME = "workApplyInfo";
    /**
     * 工单不存在提示语.
     */
    private static final String APPLY_NOT_EXIST = "当前工单不存在！id:";
    /**
     * 分任务不存在提示语.
     */
    private static final String SUBTASK_NOT_EXIST = "分任务不存在！id:";

    /**
     * 调度服务.
     * 用于催办
     */
    @Autowired
    private ScheduleService scheduleService;

    /**
     * 工单处理业务接口.
     */
    @Autowired
    private WorkApplyInfoService workApplyInfoService;

    /**
     * json工具类.
     */
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 流程业务接口.
     */
    @Autowired
    private UniFlowService uniFlowService;

    /**
     * 子任务接口.
     */
    @Autowired
    private WorkSubTaskHandleService workSubTaskHandleService;

    /**
     * 操作附件的接口。
     */
    @Autowired
    private AttachmentService attachmentService;

    /**
     * 查询工单信息.
     * @param request http请求
     * @param vo 查询参数
     * @return 处理结果
     */
    @PostMapping("/queryTaskList")
    public ResponseResult queryTaskList(final HttpServletRequest request, @RequestBody WorkListQueryVo vo) {
        ResponseResult res = new ResponseResult();
        try {
            Preconditions.checkNotNull(vo, "查询参数不能为空！");
            LoginUserPojo user = checkCurrentUser(request);
            switch (vo.getType()) {
                case DRAFT:
                    WorkApplyQueryVO queryVo = new WorkApplyQueryVO();
                    queryVo.setPage(vo.getPage());
                    queryVo.setPageSize(vo.getPageSize());
                    queryVo.setCreatorUid(user.getUserID());
                    queryVo.setStatus(WorkStatusEnum.DRAFT);
                    List<WorkApplyInfo> result = workApplyInfoService.queryWorkApply(queryVo);
                    res.setData(result);
                    break;
                case DONE_TASK:
                    res.setData(doneTasks(user, vo));
                    break;
                case TODO_TASK:
                    res.setData(todoTasks(user, vo));
                    break;
                case MY_APPLICANT:
                    res.setData(myApplicants(user, vo));
                    break;
                case TO_READ:
                case HAS_READ:
                    res.setData(readTask(user, vo));
                    break;

            }
        } catch (Exception e) {
            LOGGER.error("查询工单列表信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("查询工单列表信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 查询工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @GetMapping("/findWorkApplyById")
    public ResponseResult findWorkApplyById(final HttpServletRequest request, @RequestParam("id") String id) {
        ResponseResult res = new ResponseResult();
        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(id), "待查询的工单id为空！");
            checkCurrentUser(request);
            res.setData(workApplyInfoService.findWorkApplyInfoByIdWithMainApply(id));
        } catch (Exception e) {
            LOGGER.error("查询工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("查询工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 复制工单信息
     * @param request http请求
     * @return 处理结果
     */
    @GetMapping("/copyWorkApplyById")
    public ResponseResult copyWorkApplyById(final HttpServletRequest request, @RequestParam("id") final String id) {
        final ResponseResult result = new ResponseResult();
        try {
            final LoginUserPojo user = checkCurrentUser(request);
            // 查询原工单信息数据
            Preconditions.checkArgument(StringUtils.isNotBlank(id), "待复制的工单id为空！");
            final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoByIdWithMainApply(id);
            // 将数据传入，复制所需的数据和生成新的数据关联后，保存并返回新工单数据
            Preconditions.checkNotNull(info, "查询原工单信息数据为空！");
            workApplyInfoService.saveDraft(info, user, request, Constants.SAVE_TYPE_COPY);
            result.setData(info);
        } catch (final IllegalArgumentException | NullPointerException | WorkApplyException e) {
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("复制工单信息失败！");
            LOGGER.error(result.getMessage() + "，工单id：{}", id, e);
        }
        return result;
    }

    /**
     * 通过父工单id查询子工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @GetMapping("/findWorkApplyByParentId")
    public ResponseResult findWorkApplyByParentId(final HttpServletRequest request, @RequestParam("parentId") String parentId) {
        ResponseResult res = new ResponseResult();
        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(parentId), "待查询的父工单id为空！");
            checkCurrentUser(request);
            res.setData(workApplyInfoService.findWorkApplyByParentId(parentId));
        } catch (Exception e) {
            LOGGER.error("查询工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("查询工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 保存草稿工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/saveDraft")
    public ResponseResult saveDraft(final HttpServletRequest request, @RequestParam(WORK_APPLY_INFO_JSON_NAME) final String workApplyInfo) {
        final ResponseResult result = new ResponseResult();
        try {
            final LoginUserPojo user = checkCurrentUser(request);
            final WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            checkData(info);
            info.setFlowType(BizConstant.WISDOM_MAIN_PROCESS_CODE);
            workApplyInfoService.saveDraft(info, user, request, Constants.SAVE_TYPE_SAVE);
            result.setData(info.getId());
        } catch (final JsonProcessingException e) {
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("工单数据格式化出错！");
            LOGGER.error(result.getMessage() + "，工单数据：{}", workApplyInfo, e);
        } catch (final IllegalArgumentException | NullPointerException e) {
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("保存工单信息失败！");
            LOGGER.error(result.getMessage() + "，工单数据：{}", workApplyInfo, e);
        }
        return result;
    }

    /**
     * 数据校验.
     * @param workApplyInfo 工单信息
     */
    private void checkData(final WorkApplyInfo workApplyInfo) {
        StringBuilder errorMsg = new StringBuilder();
        if (StringUtils.isBlank(workApplyInfo.getTitle())) {
            errorMsg.append("标题不能为空！").append("\n");
        }
        if (StringUtils.isBlank(workApplyInfo.getParentId())
                && StringUtils.isBlank(workApplyInfo.getReceiverUid())) {
            errorMsg.append("主送人不能为空！").append("\n");
        }
        if (StringUtils.isBlank(workApplyInfo.getMainTaskName())) {
            errorMsg.append("主任务名称不能为空！").append("\n");
        }
        if (workApplyInfo.getParentId() == null
                && CollectionUtils.isEmpty(workApplyInfo.getSubTasks())) {
            errorMsg.append("分任务列表不能为空！").append("\n");
        }
        //校验子任务
        checkSubTasks(workApplyInfo, errorMsg);

        String error = errorMsg.toString();
        if (StringUtils.isNotBlank(error)) {
            Preconditions.checkArgument(StringUtils.isBlank(error), error);
        }
    }

    /**
     * 校验子任务.
     * @param workApplyInfo 工单信息
     * @param errorMsg 错误提示
     */
    private void checkSubTasks(WorkApplyInfo workApplyInfo, StringBuilder errorMsg) {
        if (CollectionUtils.isNotEmpty(workApplyInfo.getSubTasks())) {
            workApplyInfo.setSubTasks(workApplyInfo.getSubTasks().stream()
                    .filter(it -> StringUtils.isNotBlank(it.getName()))
                    .collect(Collectors.toList()));
            final Date now = new Date();
            int index = 0;
            for (WorkSubTaskInfo it : workApplyInfo.getSubTasks()) {
                StringBuilder subErrorMsg = new StringBuilder();
                if (StringUtils.isBlank(it.getName())) {
                    subErrorMsg.append("名称不能为空！");
                }
                if (it.getFinishTime() == null) {
                    subErrorMsg.append("完成时间不能为空！");
                } else if (it.getFinishTime().before(now)) {
                    subErrorMsg.append("完成时间不能早于当前时间！");
                }
                String subTaskErrorMsg = subErrorMsg.toString();
                if (StringUtils.isNotBlank(subTaskErrorMsg)) {
                    errorMsg.append("第").append(index + 1).append("个分任务").append(subTaskErrorMsg).append("\n");
                }
                index ++;
            }
        }
    }

    /**
     * 下载文件.
     *
     * @param request  http请求
     * @param response 响应
     * @param attachmentId     附件id
     */
    @GetMapping("/downloadAttachment")
    public void downloadAttachment(final HttpServletRequest request, final HttpServletResponse response,
                                   @RequestParam("attachmentId") final String attachmentId) {
        try {
            //获取将要插入答案所属文件的题
            Preconditions.checkArgument(StringUtils.isNotBlank(attachmentId), "待下载文件id不能为空！");
            WorkApplyInfoAttachment attachment = workApplyInfoService.findWorkAttachmentById(attachmentId);
            if (attachment != null) {
                // 非常重要
                response.reset();
                String userAgent = request.getHeader("User-Agent");
                String name = attachment.getFileName();
                // 针对IE或者以IE为内核的浏览器：
                if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
                    name = java.net.URLEncoder.encode(name, StandardCharsets.UTF_8.name());
                } else {
                    // 非IE浏览器的处理：
                    name = new String(name.getBytes(StandardCharsets.UTF_8.name()), StandardCharsets.ISO_8859_1.name());
                }
                response.setContentType("application/x-msdownload");
                response.setHeader("Content-Disposition", "attachment; filename=" + name);
                OutputStream out = response.getOutputStream();
                byte[] buffer = attachment.getAttachment();
                out.write(buffer, 0, buffer.length);
                out.close();
            } else {
                response.setContentType("text/plain; charset=utf-8");
                response.getWriter().write("文件下载失败");
            }
        } catch (Exception ex) {
            LOGGER.error("下载异常,id:{}", attachmentId, ex);
        }
    }

    /**
     * 批量下载
     *
     * @param ids  用于查询相关表获取文件信息
     * @param request  request
     * @param response response
     */
    @PostMapping("/batchDownload")
    public void batchDownload(@RequestParam("ids") final List<String> ids, final HttpServletRequest request, final HttpServletResponse response) throws IOException {
        // ids为前端传过来的的数据id集合
        if (CollectionUtils.isEmpty(ids)) {
            LOGGER.info("前端传过来的的数据id集合为空。");
            return;
        }
        final List<WorkApplyInfoAttachment> attachments = workApplyInfoService.findWorkApplyInfoAttachmentsByApplyIds(ids, AttachTypeEnum.RESULT);
        if (CollectionUtils.isEmpty(attachments)) {
            response.setContentType("text/plain; charset=utf-8");
            response.getWriter().write("文件下载失败");
            return;
        }
        // 设置响应头
        setResponseHeaders(response, request);
        try {
            final OutputStream outputStream = response.getOutputStream();
            final byte[] dataByteArr = FileUtils.zipFile(attachments);
            outputStream.write(dataByteArr);
            response.flushBuffer();
        } catch (Exception e) {
            LOGGER.error("压缩zip数据出现异常![{}]", e.getMessage());
        }
    }

    /**
     * 获取下载文件名
     *
     * @param request 请求
     * @return 下载文件名
     * @throws UnsupportedEncodingException 不支持的编码异常
     */
    private String getDownloadName(final HttpServletRequest request) throws UnsupportedEncodingException {
        String downloadName = UUID.randomUUID().toString() + ".zip";
        final String userAgent = request.getHeader("User-Agent");
        if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
            downloadName = java.net.URLEncoder.encode(downloadName, StandardCharsets.UTF_8.name());
        } else {
            downloadName = new String(downloadName.getBytes(StandardCharsets.UTF_8.name()), StandardCharsets.ISO_8859_1.name());
        }
        return downloadName;
    }

    /**
     * 设置响应头
     *
     * @param response 响应
     * @param request  请求
     * @throws UnsupportedEncodingException 不支持的编码异常
     */
    private void setResponseHeaders(final HttpServletResponse response, final HttpServletRequest request) throws UnsupportedEncodingException {
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.addHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.addHeader("Access-Control-Expose-Headers", "token,uid,Content-Disposition");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        response.addHeader("Access-Control-Allow-Headers", "Content-Type");
        response.addHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Content-Disposition", "attachment;fileName=" + getDownloadName(request));
        response.setContentType("application/octet-stream");
    }

    /**
     * 提交草稿工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/submitDraft")
    public ResponseResult submitDraft(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 提交草稿工单
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            checkData(info);
            info.setSubTasks(info.getSubTasks().stream().filter(it -> StringUtils.isNotBlank(it.getName()))
                    .collect(Collectors.toList()));
            String taskId = request.getParameter(TASK_ID_NAME);
            String opinion = request.getParameter(OPINION_NAME);
            workApplyInfoService.submitDraft(info, taskId, opinion, user, request, "");
        } catch (Exception e) {
            LOGGER.error("提交草稿工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交草稿工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 提交工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/submitTask")
    public ResponseResult submitTask(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 提交工单
            String opinion = request.getParameter(OPINION_NAME);
            WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");

            if (info.getStatus() == WorkStatusEnum.DEPT_ADMIN_APPROVE) {
                this.workApplyInfoService.submit(taskId, info, WorkStatusEnum.CREATE_SUB_FLOW, opinion, user);
            } else if (info.getStatus() == WorkStatusEnum.RESPONDER_APPROVE) {
                this.workApplyInfoService.responderApprove(taskId, info, opinion, user, request);
            } else if (info.getStatus() == WorkStatusEnum.SUB_ORG_USER_CONFIRM) {
                this.workApplyInfoService.subOrgUserConfirm(info, taskId, user, opinion, request);
            }
        } catch (Exception e) {
            LOGGER.error("提交工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 确认单个子工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/confirmSimpleTask")
    public ResponseResult confirmSimpleTask(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 确认上报工单
            String opinion = request.getParameter(OPINION_NAME);
            WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
            String subApplyId = getAndCheckParam(request, "subApplyId", "待退回子工单信息id");
            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
            this.workApplyInfoService.confirmSimpleTask(taskId, info, subApplyId, opinion, user, request);
        } catch (Exception e) {
            LOGGER.error("提交工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 转交工单信息.
     * @param request http请求信息
     * @param opinion 审批意见
     * @param workApplyId 工单id
     * @param taskId 任务id
     * @param transferUserId 转交人id
     * @param transferUserName 转交人中文名
     * @param transferOrgShortName 转交人组织短名称
     * @param transferType 转交人类型
     * @return 处理结果
     */
    @PostMapping("/transferTask")
    public ResponseResult transferTask(final HttpServletRequest request, @RequestParam(OPINION_NAME) final String opinion,
                                       @RequestParam(WORK_APPLY_ID_NAME) final String workApplyId, @RequestParam(TASK_ID_NAME) final String taskId,
                                       @RequestParam(Constants.TRANSFER_USER_ID) final String transferUserId, @RequestParam(Constants.TRANSFER_USER_NAME) final String transferUserName,
                                       @RequestParam(Constants.TRANSFER_ORG_SHORT_NAME) final String transferOrgShortName, @RequestParam(Constants.TRANSFER_TYPE) final String transferType) {
        try {
            // 验证并获取当前用户
            final LoginUserPojo user = checkCurrentUser(request);
            // 校验数据完整性
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            // 获得并校验工单对象
            final WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
            // 构建转交人信息对象
            final SelectUser transferUser = new SelectUser(transferUserId, transferUserName, transferOrgShortName, transferType);
            // 转交工单
            this.workApplyInfoService.transfer(taskId, info, StringUtils.defaultIfBlank(opinion, ApproveConstant.DEFAULT_TRANSFER_NAME), user, transferUser);
        } catch (final IllegalArgumentException | NullPointerException | WorkApplyException e) {
            final ResponseResult result = new ResponseResult();
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("转交工单信息失败！");
            LOGGER.error(result.getMessage() + "，工单id：{}，转交人id{}", workApplyId, transferUserId, e);
            return result;
        }
        return new ResponseResult();
    }

    /**
     * 退回工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/rejectTask")
    public ResponseResult rejectTask(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);

            WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
            String opinion = getAndCheckOpinion(request, true);
            // 退回工单
            if (info.getStatus() == WorkStatusEnum.DEPT_ADMIN_APPROVE) {
                // 部门管理员退回
                this.workApplyInfoService.rejectTask(info.getId(), taskId, opinion, user);
            } else if (info.getStatus() == WorkStatusEnum.RESPONDER_APPROVE
                    || info.getStatus() == WorkStatusEnum.SUB_ORG_USER_CONFIRM) {
                // 责任人确认/子流程拟稿人确认退回
                this.workApplyInfoService.rejectOrgHandler(info, taskId, opinion, user);
            }
        } catch (Exception e) {
            LOGGER.error("退回工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("退回工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 退回工单单个子工单.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/rejectSimpleTask")
    public ResponseResult rejectSimpleTask(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
            String subApplyId = getAndCheckParam(request, "subApplyId", "待退回子工单信息id");
            String opinion = getAndCheckOpinion(request, true);
            this.workApplyInfoService.rejectSimpleOrgHandler(info, taskId, subApplyId, opinion, user);
        } catch (Exception e) {
            LOGGER.error("退回工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("退回工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 分任务子流程组织人员办理环节上报.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/report")
    public ResponseResult report(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 提交上报工单
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            WorkApplyInfo oldWorkApply = getOldWorkApply(info);

            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
            final String opinion = request.getParameter(OPINION_NAME);
            checkData(oldWorkApply);
            workApplyInfoService.report(oldWorkApply, taskId, user, opinion, request);
        } catch (Exception e) {
            LOGGER.error("组织人员办理上报异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("组织人员办理上报异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 分任务子流程组织人员确认环节确认.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/confirm")
    public ResponseResult confirm(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 提交草稿工单
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            WorkApplyInfo oldWorkApply = getOldWorkApply(info);
            String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
            final String opinion = request.getParameter(OPINION_NAME);
            checkData(oldWorkApply);
            workApplyInfoService.subOrgUserConfirm(oldWorkApply, taskId, user, opinion, request);
        } catch (Exception e) {
            LOGGER.error("组织人员办理上报异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("组织人员办理上报异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 分任务子流程组织人员办理环节分派.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/dispatch")
    public ResponseResult dispatch(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            // 提交草稿工单
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            WorkApplyInfo oldWorkApply = this.workApplyInfoService.findWorkApplyInfoById(info.getId());
            Preconditions.checkNotNull(oldWorkApply, APPLY_NOT_EXIST + info.getId());
            Preconditions.checkArgument(StringUtils.isNotBlank(info.getReceiverUid()), "主送对象不能为空！");
            Preconditions.checkArgument(!StringUtils.equals(
                    UserSelectItemUtils.convertToUidJoin(info.getReceiverName()), oldWorkApply.getAssignUserId()),
                    "分派的主送人为当前用户，无须分派，请选择需要分派的主送人！");
            oldWorkApply.setReceiverUid(info.getReceiverUid());
            oldWorkApply.setReceiverName(info.getReceiverName());
            oldWorkApply.setCopySubmit(info.getCopySubmit());
            final String taskId = request.getParameter(TASK_ID_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            final String opinion = request.getParameter(OPINION_NAME);
            checkData(info);
            workApplyInfoService.dispatch(oldWorkApply, taskId, user, opinion);
        } catch (Exception e) {
            LOGGER.error("组织人员办理环节分派异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("组织人员办理环节分派异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 查询任务工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @GetMapping("/getTaskByTiid")
    public ResponseResult getTaskByTiid(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        try {
            LoginUserPojo user = checkCurrentUser(request);
            String taskId = request.getParameter(TASK_ID_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            ResultInfo resultInfo = this.uniFlowService.getTask(taskId);
            Preconditions.checkArgument(resultInfo != null && resultInfo.isResult(),
                    resultInfo != null ? resultInfo.getInfo() : "系统异常");
            TaskObject task = (TaskObject) resultInfo.getData();
            if(StringUtils.isBlank(request.getParameter(OPEN_ALL_TASK_NAME))){
                Preconditions.checkArgument(task.getOwner() != null
                                && StringUtils.equals(user.getUserID(), task.getOwner().getUserId()),
                        "当前任务的处理人非当前用户，无法操作！");
            }
            res.setData(task);
        } catch (Exception e) {
            LOGGER.error("办结工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("办结工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 待办列表.
     * @param user 当前用户
     * @param vo 查询参数
     * @return 处理结果
     */
    private Object todoTasks(final LoginUserPojo user, final WorkListQueryVo vo) {
        TaskQueryParam param = new TaskQueryParam();
        param.setStates(new int[] { TaskObject.STATE_READY });
        param.setBusinessProcesses(new String[] {BizConstant.WISDOM_MAIN_PROCESS_CODE,
                BizConstant.WISDOM_SUB_FLOW_PROCESS_CODE, BizConstant.CHAPTER_APPLY_PROCESS_CODE, BizConstant.PARTY_BRANCH_PROCESS_CODE, BizConstant.PARTY_COMMITTEE_PROCESS_CODE});
        param.setOwner(user.getUserID());
        // 设置工单标题模糊查询
        param.setParamFlag(TaskQueryParam.QUERYPARAM_APPLYTITLE_LIKE);
        param.setApplyTitle(vo.getQueryText());
        ResultInfo resultInfo = uniFlowService.queryTasksByPageForWorkCenter(param, vo.getPage(), vo.getPageSize());
        Preconditions.checkArgument(resultInfo != null && resultInfo.isResult(), resultInfo != null ? resultInfo.getInfo() : "系统异常");
        return resultInfo.getData();
    }

    /**
     * 我的申请.
     * @param user 当前用户
     * @param vo 查询参数
     * @return 处理结果
     */
    private Object myApplicants(final LoginUserPojo user, final WorkListQueryVo vo) {
        /*
        我的工单，原来从流程平台取，改成从数据库取，因为没有当前处理人信息
         */
        WorkApplyQueryVO param = new WorkApplyQueryVO();
        BeanUtils.copyProperties(vo, param);
        param.setCreatorUid(user.getUserID());
        param.setOrderType("DESC");
        param.setOrderField("UPDATE_TIME");
        param.setExcludeDraft(true);
        param.setApplyTitle(vo.getQueryText());
        return new PageInfo(workApplyInfoService.queryWorkApply(param));
    }

    /**
     * 我的已办.
     * @param user 当前用户
     * @param vo 查询参数
     * @return 处理结果
     */
    private Object doneTasks(final LoginUserPojo user, final WorkListQueryVo vo) {
        WorkApplyQueryVO param = new WorkApplyQueryVO();
        BeanUtils.copyProperties(vo, param);
        param.setApproveUid(user.getUserID());
        param.setOrderType("DESC");
        param.setOrderField("o.update_time");
        param.setApplyTitle(vo.getQueryText());
        List<WorkApplyInfo> tasks = workApplyInfoService.queryDoneTask(param);
        return new PageInfo(tasks);
    }

    /**
     * 查询阅办任务.
     * @param user 当前用户
     * @param vo 查询参数
     * @return 处理结果
     */
    private Object readTask(final LoginUserPojo user, final WorkListQueryVo vo) {
        final WorkApplyQueryVO param = new WorkApplyQueryVO();
        BeanUtils.copyProperties(vo, param);
        param.setUserId(user.getUserID());
        param.setOrderType("DESC");
        param.setOrderField("o.update_time");
        param.setApplyTitle(vo.getQueryText());
        param.setWorkType(vo.getType().getValue());
        final List<WorkApplyInfo> tasks = workApplyInfoService.queryReadTask(param);
        return new PageInfo(tasks);
    }

    /**
     * 修改阅办状态
     * @param request request
     * @param workApplyId 工单id
     * @return
     */
    @GetMapping("/changeReadType")
    public ResponseResult changeReadType(final HttpServletRequest request, @RequestParam("workApplyId") final String workApplyId) {
        final LoginUserPojo user = checkCurrentUser(request);
        workApplyInfoService.updateReadType(workApplyId, user);
        return new ResponseResult();
    }

    /**
     * 获取并校验当前用户.
     * @param request http请求
     * @return 当前用户
     */
    private LoginUserPojo checkCurrentUser(final HttpServletRequest request) {
        LoginUserPojo user = LoginUtils.getCurrentUserByToken(request.getHeader(Constants.TOKEN_NAME));
        Assert.notNull(user, "用户未登录或登录信息不存在");
        return user;
    }

    /**
     * 查询指定用户下所有待办子任务的要求完成时间.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/findWorkSubTasksByUserId")
    public ResponseResult findWorkSubTasksByUserId(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        LoginUserPojo user = checkCurrentUser(request);
        List<WorkSubTaskInfo> list = workApplyInfoService.findWorkSubTasksByUserId(user.getUserID());
        if(CollectionUtils.isNotEmpty(list)) {
            List<String> dateList = list.stream().map(workSubTaskInfo -> {
                // 将时间戳转化为指定日期格式
                Date finishTime = workSubTaskInfo.getFinishTime();
                LocalDate localDate = finishTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constants.DATE_FORMAT_DEFAULT);
                return localDate.format(formatter);
            }).distinct().collect(Collectors.toList());
            res.setData(dateList);
        }
        return res;
    }

    /**
     * 对下级分任务做确认或退回操作.
     * 只有确认或者退回两种情况
     * @param request http请求
     * @param confirm 是否确认
     * @return 处理结果
     */
    @PostMapping("/dealSubTask")
    public SubTaskOperationResult dealSubTask(final HttpServletRequest request, final boolean confirm) {
        SubTaskOperationResult res = new SubTaskOperationResult();
        LoginUserPojo user = checkCurrentUser(request);

        WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
        List<WorkSubTaskInfo> subTaskInfoList = getAndCheckWorkSubTaskInfoList(request);
        String opinion = getAndCheckOpinion(request, !confirm);

        if (confirm) {
            // 确认操作，因为涉及到影响父级分任务，所以连通父级分任务也要一起返回，省得前端再查询一次
            Pair<List<WorkSubTaskInfo>, List<WorkSubTaskInfo>> resultPair = workSubTaskHandleService.confirm(info, subTaskInfoList, opinion, user);
            // 确认的分任务
            res.setSubTaskInfoList(resultPair.getLeft());
            // 受影响的父任务（已带上合并附件）
            res.setParentSubTaskInfoList(resultPair.getRight());
        } else {
            // 退回操作，只需要返回退回的分任务列表即可
            res.setSubTaskInfoList(workSubTaskHandleService.reject(info, subTaskInfoList, opinion, user));
        }
        // 设置父级工单状态、省得再查一次了
        res.setParentWorkApplyInfoStatus(info.getStatus());
        res.setParentWorkApplyHandler(info.getHandlerName());
        // tiid有可能发生变化，要返给前端
        res.setTiid(info.getTiid());

        return res;
    }

    @GetMapping("/deleteAttachmentById/{fileId}")
    public ResponseResult deleteAttachmentById(@PathVariable String fileId) {
        ResponseResult res = new ResponseResult();
        try {
            attachmentService.deleteAttachmentById(fileId);
        } catch (Exception e) {
            LOGGER.error(String.format("delete attachment fail, fileId is [%s]", fileId), e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("删除附件失败，请联系系统管理员！");
        }

        return res;
    }

    /**
     * 上报分任务.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/reportSubTask")
    public SubTaskOperationResult reportSubTask(final HttpServletRequest request) {
        final SubTaskOperationResult res = new SubTaskOperationResult();
        final LoginUserPojo user = checkCurrentUser(request);

        final WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
        final List<WorkSubTaskInfo> subTaskInfoList = getAndCheckWorkSubTaskInfoList(request);
        final String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
        final String opinion = getAndCheckOpinion(request, false);

        //是否使用合并报表上报，否的话要验证上报附件
        final String reportMergeAttachments = request.getParameter("reportMergeAttachments");
        final List<Boolean> reportMergeAttachmentList = Arrays.stream(StringUtils.split(reportMergeAttachments, Constants.STRING_SPLIT))
            .map(BooleanUtils::toBoolean)
            .collect(Collectors.toList());

        // 给分任务对象设置是否需要合并报表附件上传
        for (final ListIterator taskList = subTaskInfoList.listIterator(),
            reportMerge = reportMergeAttachmentList.listIterator(); taskList.hasNext();) {
            WorkSubTaskInfo workSubTaskInfo = (WorkSubTaskInfo)taskList.next();
            workSubTaskInfo.setReportMergeAttachment((Boolean)reportMerge.next());
        }
        // 调用上报方法，得到的结果依次是：当前工单，已经上报了的分任务列表，父工单
        final Triple<WorkApplyInfo, List<WorkSubTaskInfo>, WorkApplyInfo> reportResult =
                workSubTaskHandleService.report(info, subTaskInfoList, taskId, opinion, user, request);

        // 设置返回对象
        res.setSubTaskInfoList(reportResult.getMiddle());
        // 这里要返回当前工单的状态，但处理要返回父工单的，因为当当前工单状态发生变化时（提交到拟稿人确认，处理人是admin），实际的处理人是在父工单上
        res.setParentWorkApplyInfoStatus(reportResult.getLeft().getStatus());
        res.setParentWorkApplyHandler(reportResult.getRight().getHandlerName());

        return res;
    }

    /**
     * 更新通知方式.
     * @param request 请求
     * @return 响应
     */
    @PostMapping("/updateNotifyMethod")
    public ResponseResult updateNotifyMethod(final HttpServletRequest request) {
        final LoginUserPojo user = checkCurrentUser(request);
        final WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
        //判断权限
        Preconditions.checkArgument(WorkApplyOperationUtils.haveOperatePermission(info, user), "您没有操作该工单的权限！");
        final String notifyMethodStr = request.getParameter(NOTIFY_AFTER_REPORT_ALL);
        Preconditions.checkArgument(StringUtils.isNotBlank(notifyMethodStr), "通知方式不能为空");
        final boolean notifyMethod = BooleanUtils.toBoolean(notifyMethodStr);
        workApplyInfoService.updateNotifyMethod(info.getId(), notifyMethod);
        return new ResponseResult();
    }

    /**
     * 检查获取主送人.
     * 检查党组织是否有干事
     * 选择的人员和党组织干事是否重复
     * @param orgUserSelectItems 先人框信息
     * @return 解析后的人员数据
     */
    @PostMapping("/checkAndGetReceiver")
    public ResponseResult checkAndGetReceiver(@RequestBody List<PartyOrgUserSelectItem> orgUserSelectItems) {
        final ResponseResult res = new ResponseResult();
        final List<List<PartyUserVo>> users = workApplyInfoService.getResolveReceiver(orgUserSelectItems);
        final String checkInfo = workApplyInfoService.checkReceiver(orgUserSelectItems, users);
        if (StringUtils.isEmpty(checkInfo)) {
            res.setData(users);
        } else {
            res.setMessage(checkInfo);
            res.setResult(ResponseResult.FAILED_RESULT);
        }
        return res;
    }

    /**
     * 主工单办结.
     * 办结确认状态拟稿人办结
     * @param request http请求
     * @return 响应
     */
    @PostMapping("/finishConfirm")
    public ResponseResult finishConfirm(final HttpServletRequest request) {
        ResponseResult res = new ResponseResult();
        LoginUserPojo user = checkCurrentUser(request);
        String taskId = getAndCheckParam(request, TASK_ID_NAME, "待办任务id");
        String opinion = getAndCheckOpinion(request, false);
        WorkApplyInfo info = getAndCheckWorkApplyInfo(request);
        info.setTiid(taskId);
        workApplyInfoService.finishConfirm(info, opinion, user);
        return res;
    }
    /**
     * 催办任务.
     * @param request http请求
     * @return 响应
     */
    @PostMapping("/urgeTask")
    public ResponseResult urgeTask(final HttpServletRequest request) {
        final ResponseResult res = new ResponseResult();
        final LoginUserPojo user = checkCurrentUser(request);
        final String subTaskIds = getAndCheckParam(request, SUBTASK_ID_NAMES, "分任务ID");
        final List<String> idList = Arrays.asList(StringUtils.split(subTaskIds, Constants.STRING_SPLIT));
        final boolean result = scheduleService.manualUrge(idList, user); //注释
        if (!result) {
           res.setResult(ResponseResult.FAILED_RESULT);
           res.setMessage("所有办理人均已办理此分任务，不用催办。");
        }
        return res;
    }

    /**
     * 结报.
     * 起草人可以提前终止流程：
     *    需要递归查询所有工单信息，需要异步处理
     *    处理流程平台待办
     *    需要更新工单及分任务状态，及handler信息
     *    需要记录已办记录
     * @param request http请求
     * @return 响应
     */
    @PostMapping("/terminateApply")
    public ResponseResult terminateApply(final HttpServletRequest request) {
        final ResponseResult res = new ResponseResult();
        final LoginUserPojo user = checkCurrentUser(request);
        String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        workApplyInfoService.terminateApply(workApplyId, user);
        return res;
    }

    /**
     * 删除草稿.
     * @param request
     * @return
     */
    @PostMapping("/deleteDraft")
    public ResponseResult deleteDraft(final HttpServletRequest request) {
        final ResponseResult res = new ResponseResult();
        final LoginUserPojo user = checkCurrentUser(request);
        final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("deleteDraft workApplyId:{}", LoggerHelper.replaceBreakCharacters(workApplyId));
        }
        workApplyInfoService.deleteDraft(workApplyId, user);
        return res;
    }

    /**
     * 获得并校验工单对象。
     * <p>参数名：workApplyId</p>
     * <p>对应的参数名为空、或者根据参数值取到null，都会报错</p>
     * @param request request请求对象
     * @return 工单对象
     */
    private WorkApplyInfo getAndCheckWorkApplyInfo(final HttpServletRequest request) {
        String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), APPLY_NOT_EXIST + workApplyId);
        WorkApplyInfo info = this.workApplyInfoService.findWorkApplyInfoById(workApplyId);
        Preconditions.checkNotNull(info, APPLY_NOT_EXIST + workApplyId);
        // 处理前端传上来的tiid，注意这个tiid只是传递参数用，不存数据库的
        info.setTiid(request.getParameter(TASK_ID_NAME));
        return info;
    }

    /**
     * 获得并校验分任务对象列表。
     * <p>参数名：subTaskIds，支持多值，多值以半角逗号分隔</p>
     * <p>对应的参数名为空、或者根据参数值取到空列表，都会报错</p>
     * @param request request请求对象
     * @return 分任务对象列表
     */
    private List<WorkSubTaskInfo> getAndCheckWorkSubTaskInfoList(final HttpServletRequest request) {
        String subTaskIds = request.getParameter(SUBTASK_ID_NAMES);
        Preconditions.checkArgument(StringUtils.isNotBlank(subTaskIds), TASK_ID_BLANK_ERROR_MSG);
        List<String> idList = Arrays.asList(StringUtils.split(subTaskIds, Constants.STRING_SPLIT));
        List<WorkSubTaskInfo> subTaskInfoList = workApplyInfoService.findWorkSubTasksByIds(idList);
        Preconditions.checkArgument(CollectionUtils.isNotEmpty(subTaskInfoList), SUBTASK_NOT_EXIST + subTaskIds);
        return subTaskInfoList;
    }

    /**
     * 获得并校验处理意见。
     * @param request request请求对象
     * @param checkNotBlank 是否要判空，如果需要判空（传值true）、且实际拿到空值，会报错
     * @return 处理意见
     */
    private String getAndCheckOpinion(final HttpServletRequest request, boolean checkNotBlank) {
        String opinion = request.getParameter(OPINION_NAME);
        if(checkNotBlank) {
            Preconditions.checkArgument(StringUtils.isNotBlank(opinion), "处理意见不能为空！");
        }
        return opinion;
    }

    /**
     * 获得并校验某个参数。
     * @param request request请求对象
     * @param paramName 参数名
     * @param objectName 参数对应的对象名称，用途：根据参数名拿到的参数值是空的时候，用这个对象名称来报错
     * @return 参数值
     */
    private String getAndCheckParam(final HttpServletRequest request, String paramName, String objectName) {
        String paramValue = request.getParameter(paramName);
        Preconditions.checkArgument(StringUtils.isNotBlank(paramValue), objectName + "不能为空！");
        return paramValue;
    }

    /**
     * 获得旧工单对象。
     * <p>所谓旧工单对象，就是根据id从数据库里查出来的、还未变更过的对象</p>
     * <p>并且将新的工单对象的分任务copy到旧对象里</p>
     * @param newInfo 新的工单对象
     * @return 旧工单对象
     */
    private WorkApplyInfo getOldWorkApply(WorkApplyInfo newInfo) {
        WorkApplyInfo oldWorkApply = this.workApplyInfoService.findWorkApplyInfoById(newInfo.getId());
        Preconditions.checkNotNull(oldWorkApply, APPLY_NOT_EXIST + newInfo.getId());
        if (CollectionUtils.isNotEmpty(newInfo.getSubTasks())) {
            oldWorkApply.setSubTasks(newInfo.getSubTasks().
                    stream().filter(it -> StringUtils.isNotBlank(it.getName())).collect(Collectors.toList()));
        }
        return oldWorkApply;
    }
}
